# uncompyle6 version 3.2.3
# Python bytecode 3.6 (3379)
# Decompiled from: Python 3.6.8 |Anaconda custom (64-bit)| (default, Feb 21 2019, 18:30:04) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: site-packages\PyInstaller\archive\readers.py
"""
This CArchiveReader is used only by the archieve_viewer utility.
"""
import struct
from PyInstaller.loader.pyimod02_archive import (
    ArchiveReader,
    PYZ_TYPE_MODULE,
    PYZ_TYPE_PKG,
    PYZ_TYPE_DATA,
)


class NotAnArchiveError(Exception):
    pass


class CTOCReader(object):
    """
    A class encapsulating the table of contents of a CArchive.
    
    When written to disk, it is easily read from C.
    """

    ENTRYSTRUCT = "!iiiiBB"
    ENTRYLEN = struct.calcsize(ENTRYSTRUCT)

    def __init__(self):
        self.data = []

    def frombinary(self, s):
        """
        Decode the binary string into an in memory list.
        
        S is a binary string.
        """
        p = 0
        while p < len(s):
            slen, dpos, dlen, ulen, flag, typcd = struct.unpack(
                self.ENTRYSTRUCT, s[p : p + self.ENTRYLEN]
            )
            nmlen = slen - self.ENTRYLEN
            p = p + self.ENTRYLEN
            nm, = struct.unpack("%is" % nmlen, s[p : p + nmlen])
            p = p + nmlen
            nm = nm.rstrip(b"\x00")
            nm = nm.decode("utf-8")
            typcd = chr(typcd)
            self.data.append((dpos, dlen, ulen, flag, typcd, nm))

    def get(self, ndx):
        """
        Return the table of contents entry (tuple) at index NDX.
        """
        return self.data[ndx]

    def __getitem__(self, ndx):
        return self.data[ndx]

    def find(self, name):
        """
        Return the index of the toc entry with name NAME.
        
        Return -1 for failure.
        """
        for i, nm in enumerate(self.data):
            if nm[-1] == name:
                return i

        return -1


class CArchiveReader(ArchiveReader):
    """
    An Archive subclass that can hold arbitrary data.
    
    This class encapsulates all files that are bundled within an executable.
    It can contain ZlibArchive (Python .pyc files), dlls, Python C extensions
    and all other data files that are bundled in --onefile mode.
    
    Easily handled from C or from Python.
    """

    MAGIC = b"MEI\x0c\x0b\n\x0b\x0e"
    HDRLEN = 0
    LEVEL = 9
    _cookie_format = "!8siiii64s"
    _cookie_size = struct.calcsize(_cookie_format)

    def __init__(self, archive_path=None, start=0, length=0, pylib_name=""):
        """
        Constructor.
        
        archive_path path name of file (create empty CArchive if path is None).
        start        is the seekposition within PATH.
        len          is the length of the CArchive (if 0, then read till EOF).
        pylib_name   name of Python DLL which bootloader will use.
        """
        self.length = length
        self._pylib_name = pylib_name
        self.pkg_start = 0
        super(CArchiveReader, self).__init__(archive_path, start)

    def checkmagic(self):
        """
        Verify that self is a valid CArchive.
        
        Magic signature is at end of the archive.
        
        This fuction is used by ArchiveViewer.py utility.
        """
        if self.length:
            self.lib.seek(self.start + self.length, 0)
        else:
            self.lib.seek(0, 2)
        filelen = self.lib.tell()
        self.lib.seek(max(0, filelen - 4096))
        searchpos = self.lib.tell()
        buf = self.lib.read(min(filelen, 4096))
        pos = buf.rfind(self.MAGIC)
        if pos == -1:
            raise RuntimeError(
                "%s is not a valid %s archive file"
                % (self.path, self.__class__.__name__)
            )
        filelen = searchpos + pos + self._cookie_size
        magic, totallen, tocpos, toclen, pyvers, pylib_name = struct.unpack(
            self._cookie_format, buf[pos : pos + self._cookie_size]
        )
        if magic != self.MAGIC:
            raise RuntimeError(
                "%s is not a valid %s archive file"
                % (self.path, self.__class__.__name__)
            )
        self.pkg_start = filelen - totallen
        if self.length:
            if totallen != self.length or self.pkg_start != self.start:
                raise RuntimeError("Problem with embedded archive in %s" % self.path)
        if not pylib_name:
            raise RuntimeError("Python library filename not defined in archive.")
        self.tocpos, self.toclen = tocpos, toclen

    def loadtoc(self):
        """
        Load the table of contents into memory.
        """
        self.toc = CTOCReader()
        self.lib.seek(self.pkg_start + self.tocpos)
        tocstr = self.lib.read(self.toclen)
        self.toc.frombinary(tocstr)

    def extract(self, name):
        """
        Get the contents of an entry.
        
        NAME is an entry name OR the index to the TOC.
        
        Return the tuple (ispkg, contents).
        For non-Python resoures, ispkg is meaningless (and 0).
        Used by the import mechanism.
        """
        if type(name) == type(""):
            ndx = self.toc.find(name)
            if ndx == -1:
                return
        else:
            ndx = name
        dpos, dlen, ulen, flag, typcd, nm = self.toc.get(ndx)
        with self.lib:
            self.lib.seek(self.pkg_start + dpos)
            rslt = self.lib.read(dlen)
        if flag == 1:
            import zlib

            rslt = zlib.decompress(rslt)
        if typcd == "M":
            return (1, rslt)
        else:
            return (typcd == "M", rslt)

    def contents(self):
        """
        Return the names of the entries.
        """
        rslt = []
        for dpos, dlen, ulen, flag, typcd, nm in self.toc:
            rslt.append(nm)

        return rslt

    def openEmbedded(self, name):
        """
        Open a CArchive of name NAME embedded within this CArchive.
        
        This fuction is used by ArchiveViewer.py utility.
        """
        ndx = self.toc.find(name)
        if ndx == -1:
            raise KeyError("Member '%s' not found in %s" % (name, self.path))
        dpos, dlen, ulen, flag, typcd, nm = self.toc.get(ndx)
        if typcd not in "zZ":
            raise NotAnArchiveError("%s is not an archive" % name)
        if flag:
            raise ValueError("Cannot open compressed archive %s in place" % name)
        return CArchiveReader(self.path, self.pkg_start + dpos, dlen)
